package joindemo.aop;

import cn.dev33.satoken.stp.StpUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import joindemo.annotation.Log;
import joindemo.common.ResponseResult;
import joindemo.common.constants.LogConstants;
import joindemo.domain.vo.ArticleVo;
import joindemo.domain.vo.PageVo;
import joindemo.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.lang.reflect.Method;
import java.util.*;

/**
 * 操作日志记录处理切面
 * @author pym
 */
@Aspect
@Component
@Slf4j
public class LogAspect {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    static {
        // 配置 Jackson 不要在遇到空对象时抛出异常
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
    }

    /**
     * 配置切入点
     */
    @Pointcut("@annotation(joindemo.common.annotation.Log)")
    public void logPointCut() {
    }
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        Object result = null;
        try {
            result = point.proceed();
        } finally {
            long time = System.currentTimeMillis() - beginTime;
            handleLog(point, result, time);
        }
        return result;
    }

    private void handleLog(ProceedingJoinPoint joinPoint, Object result, long time) {
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            Log controllerLog = method.getAnnotation(Log.class);

            if (controllerLog == null) {
                return;
            }

            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes != null ? attributes.getRequest() : null;

            String url = (request != null) ? request.getRequestURI() : "";
            String ip = (request != null) ? request.getRemoteAddr() : "";
            String requestMethod = (request != null) ? request.getMethod() : "";

            String operator = "Anonymous";
            try {
                if (StpUtil.isLogin()) {
                    operator = StpUtil.getLoginIdAsString();
                }
            } catch (Exception ignored) {
            }

            log.info("======================================================");
            log.info(" 模块名称: {}", controllerLog.title());
            log.info(" 业务类型: {}", controllerLog.businessType());
            log.info(" 操作用户: {}", operator);
            log.info(" 请求URL : {}", url);
            log.info(" 请求方式: {}", requestMethod);
            log.info(" 客户端IP: {}", ip);
            log.info(" 方法名称: {}.{}", signature.getDeclaringTypeName(), method.getName());
            log.info(" 执行耗时: {} ms", time);

            // 处理请求参数 (带脱敏)
            if (controllerLog.isSaveRequestData()) {
                setRequestValue(joinPoint);
            }

            // 处理响应结果 (带脱敏)
            if (controllerLog.isSaveResponseData() && result != null) {
                // 1. 如果是分页结果或大文本，避免全量解析
                if (result instanceof ResponseResult) {
                    Object data = ((ResponseResult<?>) result).getData();
                    if (isBigData(data)) {
                        log.info(" 响应参数: [数据量过大，已忽略具体内容]");
                    } else {
                        // 正常脱敏记录
                        log.info(" 响应参数: {}", toJsonStringWithMasking(result));
                    }
                } else {
                    log.info(" 响应参数: {}", toJsonStringWithMasking(result));
                }
            }
            log.info("======================================================");
        } catch (Exception e) {
            log.error("==日志记录异常==", e);
        }
    }
    private boolean isBigData(Object data) {
        if (data == null) {
            return false;
        }
        // 如果是 PageVo，且列表长度 > 0，视为大数据，不记录详情
        if (data instanceof PageVo) {
            return true;
        }
        // 如果是文章详情 VO，且包含 content
        if (data instanceof ArticleVo) {
            String content = ((ArticleVo) data).getContent();
            return content != null && content.length() > 500;
        }
        return false;
    }

    private void setRequestValue(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        List<Object> logArgs = new ArrayList<>();
        for (Object arg : args) {
            if (arg instanceof HttpServletRequest || arg instanceof HttpServletResponse || arg instanceof MultipartFile) {
                continue;
            }
            logArgs.add(arg);
        }
        try {
            if (!logArgs.isEmpty()) {
                // 使用脱敏序列化
                log.info(" 请求参数: {}", toJsonStringWithMasking(logArgs));
            }
        } catch (Exception e) {
            log.warn(" 参数序列化失败");
        }
    }

    /**
     * 对象转JSON字符串（带脱敏处理）
     */
    private String toJsonStringWithMasking(Object object) {
        try {
            // 先转换为 JsonNode 树
            JsonNode rootNode = objectMapper.valueToTree(object);
            // 递归脱敏
            maskSensitiveNode(rootNode);
            return rootNode.toString();
        } catch (Exception e) {
            return String.valueOf(object);
        }
    }

    /**
     * 递归遍历并脱敏 JsonNode
     */
    private void maskSensitiveNode(JsonNode node) {
        if (node.isObject()) {
            ObjectNode objectNode = (ObjectNode) node;
            Iterator<Map.Entry<String, JsonNode>> fields = objectNode.fields();
            while (fields.hasNext()) {
                Map.Entry<String, JsonNode> field = fields.next();
                String key = field.getKey().toLowerCase();
                JsonNode value = field.getValue();

                // 检查 Key 是否敏感 (使用常量类)
                if (isSensitiveKey(key)) {
                    // 如果是敏感字段，直接替换为 ******
                    if (value.isTextual() && StringUtils.isNotEmpty(value.asText())) {
                        objectNode.set(field.getKey(), new TextNode("******"));
                    }
                } else {
                    // 否则继续递归
                    maskSensitiveNode(value);
                }
            }
        } else if (node.isArray()) {
            ArrayNode arrayNode = (ArrayNode) node;
            for (JsonNode item : arrayNode) {
                maskSensitiveNode(item);
            }
        }
    }

    /**
     * 判断 Key 是否属于敏感字段
     */
    private boolean isSensitiveKey(String key) {
        for (String sensitiveKey : LogConstants.SENSITIVE_KEYS) {
            if (key.contains(sensitiveKey)) {
                return true;
            }
        }
        return false;
    }
}